home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 12581 / 12581.xpi / components_src / trayToolkitWin.cpp < prev   
C/C++ Source or Header  |  2010-01-21  |  26KB  |  861 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is TrayToolkit
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Nils Maier
  18.  * Portions created by the Initial Developer are Copyright (C) 2008
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Nils Maier <MaierMan@web.de>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. #ifdef _WIN32_IE
  39. #    undef _WIN32_IE
  40. #endif
  41. #define _WIN32_IE 0x0600 // We want more features
  42. #include <windows.h>
  43. #include <shellapi.h>
  44.  
  45. #include "trayToolkit.h"
  46.  
  47. #include "nsCOMPtr.h"
  48. #include "nsStringAPI.h"
  49. #include "nsServiceManagerUtils.h"
  50.  
  51. #include "nsIDOMWindow.h"
  52. #include "nsIDOMDocument.h"
  53. #include "nsIDOMDocumentView.h"
  54. #include "nsIDOMAbstractView.h"
  55.  
  56. #include "nsIDOMDocumentEvent.h"
  57. #include "nsIDOMEvent.h"
  58. #include "nsIDOMEventTarget.h"
  59. #include "nsIPrivateDOMEvent.h"
  60. #include "nsIDOMMouseEvent.h"
  61.  
  62. #include "nsIWebNavigation.h"
  63. #include "nsIBaseWindow.h"
  64.  
  65. #include "nsIInterfaceRequestorUtils.h"
  66. #include "nsIObserverService.h"
  67.  
  68. #include "nsIPrefService.h"
  69. #include "nsIPrefBranch2.h"
  70.  
  71.  
  72. namespace {
  73.  
  74.     static const wchar_t kTrayMessage[]    = L"_MINTRAYR_TrayMessageW";
  75.     static const wchar_t kWrapperProp[]    = L"_MINTRAYR_WRAPPER_PTR";
  76.     static const wchar_t kWrapperOldProp[] = L"_MINTRAYR_WRAPPER_OLD_PROC";
  77.     static const wchar_t kWatcherDOMProp[] = L"_MINTRAYR_WATCHER_DOM_PTR";
  78.     static const wchar_t kWatcherOldProp[] = L"_MINTRAYR_WATCHER_OLD_PROC";
  79.     static const wchar_t kWatcherLock[]    = L"_MINTRAYR_WATCHER_LOCK";
  80.     
  81.     typedef enum _eMinimizeActions {
  82.         kTrayOnMinimize = (1 << 0),
  83.         kTrayOnClose = (1 << 1)
  84.     } eMinimizeActions;
  85.  
  86.     typedef BOOL (WINAPI *pChangeWindowMessageFilter)(UINT message, DWORD dwFlag);
  87. #ifndef MGSFLT_ADD
  88.     // Not a Vista SDK
  89.     #    define MSGFLT_ADD 1
  90.     #    define MSGFLT_REMOVE 2
  91. #endif
  92.  
  93.     static UINT WM_TASKBARCREATED = 0;
  94.     static UINT WM_TRAYMESSAGE = 0;
  95.  
  96.     /**
  97.      * Helper function that will allow us to receive some broadcast messages on Vista 
  98.      * (We need to bypass that filter if we run as Administrator, but the orginating process
  99.      * has less priviledges)
  100.      */
  101.     static void TrayServiceImpl_AdjustMessageFilters(UINT filter) 
  102.     {
  103.         HMODULE user32 = LoadLibraryW(L"user32.dll");
  104.         if (user32 != NULL) {
  105.             pChangeWindowMessageFilter changeWindowMessageFilter =
  106.                 reinterpret_cast<pChangeWindowMessageFilter>(GetProcAddress(
  107.                     user32,
  108.                     "ChangeWindowMessageFilter"
  109.                 ));
  110.             if (changeWindowMessageFilter != NULL) {
  111.                 changeWindowMessageFilter(WM_TASKBARCREATED, filter);
  112.             }
  113.             FreeLibrary(user32);
  114.         }
  115.     }
  116.  
  117.     /**
  118.      * Helper function that will try to get the nsIBaseWindow from an nsIDOMWindow
  119.      */
  120.     static NS_IMETHODIMP TrayServiceImpl_GetBaseWindow(nsIDOMWindow *aWindow, nsIBaseWindow **aBaseWindow)
  121.     {
  122.         NS_ENSURE_ARG_POINTER(aWindow);
  123.         NS_ENSURE_ARG_POINTER(aBaseWindow);
  124.  
  125.         nsresult rv;
  126.         
  127.         nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow, &rv);
  128.         NS_ENSURE_SUCCESS(rv, rv);
  129.         
  130.         nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(webNav, &rv);
  131.         NS_ENSURE_SUCCESS(rv, rv);
  132.  
  133.         *aBaseWindow = baseWindow;
  134.         NS_IF_ADDREF(*aBaseWindow);
  135.         return NS_OK;
  136.     }
  137.  
  138.     /**
  139.      * Helper class to get Windows Version information
  140.      */
  141.     class OSVersionInfo : public OSVERSIONINFOEXW
  142.     {
  143.     public:
  144.         OSVersionInfo() {
  145.             dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
  146.             ::GetVersionExW(reinterpret_cast<LPOSVERSIONINFOW>(this));
  147.         }
  148.         bool isVistaOrLater() {
  149.             return dwMajorVersion >= 6;
  150.         }
  151.     };
  152.     
  153.     static bool DoMinimizeWindow(HWND hwnd, eMinimizeActions action)
  154.     {
  155.         nsIDOMWindow *window = reinterpret_cast<nsIDOMWindow*>(::GetPropW(hwnd, kWatcherDOMProp));
  156.         if (window == 0) {            
  157.             return false;
  158.         }
  159.         
  160.         nsCOMPtr<nsIPrefBranch2> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
  161.         if (prefs) {
  162.             PRInt32 whenToMinimize = 0;
  163.             prefs->GetIntPref("extensions.mintrayr.minimizeon", &whenToMinimize);
  164.             if ((whenToMinimize & action) == 0) {
  165.                 return false;
  166.             }
  167.         }
  168.  
  169.         // We might receive the hide message, protect against it.
  170.         if (reinterpret_cast<unsigned int>(::GetPropW(hwnd, kWatcherLock)) == 0x1) {
  171.             return false;
  172.         }
  173.         ::SetPropW(hwnd, kWatcherLock, reinterpret_cast<HANDLE>(0x1));
  174.  
  175.         nsresult rv;
  176.         nsCOMPtr<trayITrayService> traySvc(do_GetService(TRAYSERVICE_CONTRACTID, &rv));
  177.         if (NS_SUCCEEDED(rv)) {
  178.             rv = traySvc->Minimize(window);
  179.         }
  180.  
  181.         ::RemovePropW(hwnd, kWatcherLock);    
  182.         return NS_SUCCEEDED(rv);
  183.     }
  184.  
  185.     /**
  186.      * Install this WindowProc in windows we watch
  187.      * 
  188.      * Note: Once installed don't remove this again (by setting the old WNDPROC)
  189.      * Or else we might break the chain if something else installed yet another WNDPROC
  190.      * And chained us.
  191.      * Only the property kWatcherDOMProp should be removed, so that the function knows when
  192.      * to handle stuff and when to simply pass it down the chain.
  193.      */
  194.     LRESULT CALLBACK TrayServiceImpl_WatchWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  195.     {
  196.         nsIDOMWindow *window = reinterpret_cast<nsIDOMWindow*>(::GetPropW(hwnd, kWatcherDOMProp));
  197.         if (window == 0) {
  198.             goto WatcherWindowProcEnd;
  199.         }
  200.     
  201.         switch (uMsg) {
  202.         case WM_CLOSE:
  203.         case WM_DESTROY: {
  204.             // The window is (about to be) destroyed
  205.             // Let the service know and unwatch the window
  206.             nsresult rv;
  207.             nsCOMPtr<trayITrayService> traySvc(do_GetService(TRAYSERVICE_CONTRACTID, &rv));
  208.             if (NS_SUCCEEDED(rv)) {
  209.                 traySvc->UnwatchMinimize(window);
  210.             }
  211.             break;
  212.         } // case WM_DESTROY
  213.         
  214.         case WM_WINDOWPOSCHANGED: {
  215.             /* XXX Fix this bit to something more reasonable
  216.                 The following code kinda replicates the way mozilla gets the window state.
  217.                 We intensionally "hide" the SW_SHOWMINIMIZED here.
  218.                 This indeed might cause some side effects, but if it didn't we couldn't open
  219.                 menus due to bugzilla #435848,.
  220.                 This might defeat said bugfix completely reverting to old behavior, but only when we're active, of course.
  221.             */
  222.             WINDOWPOS *wp = reinterpret_cast<WINDOWPOS*>(lParam);
  223.             if (wp->flags & SWP_FRAMECHANGED && ::IsWindowVisible(hwnd)) {
  224.                 WINDOWPLACEMENT pl;
  225.                 pl.length = sizeof(WINDOWPLACEMENT);
  226.                 ::GetWindowPlacement(hwnd, &pl);
  227.                 if (pl.showCmd == SW_SHOWMINIMIZED) {
  228.                     if (DoMinimizeWindow(hwnd, kTrayOnMinimize)) {
  229.                         pl.showCmd = SW_HIDE;
  230.                         ::SetWindowPlacement(hwnd, &pl);
  231.                         return 0;
  232.                     }
  233.                 }
  234.             }
  235.             break;
  236.         } // case WM_WINDOWPOSCHANGED
  237.         
  238.         case WM_NCLBUTTONDOWN:
  239.         case WM_NCLBUTTONUP:
  240.             if (wParam == HTCLOSE && DoMinimizeWindow(hwnd, kTrayOnClose)) {
  241.                 return TRUE;
  242.             }
  243.             break;
  244.  
  245.         case WM_SYSCOMMAND:
  246.             if (wParam == SC_CLOSE && DoMinimizeWindow(hwnd, kTrayOnClose)) {
  247.                 return 0;
  248.             }
  249.             break;
  250.         }
  251.  
  252. WatcherWindowProcEnd:
  253.         // Call the old WNDPROC or at lest DefWindowProc
  254.         WNDPROC oldProc = reinterpret_cast<WNDPROC>(::GetPropW(hwnd, kWatcherOldProp));
  255.         if (oldProc != NULL) {
  256.             return ::CallWindowProcW(oldProc, hwnd, uMsg, wParam, lParam);
  257.         }
  258.         return ::DefWindowProcW(hwnd, uMsg, wParam, lParam);
  259.     }
  260. }
  261.  
  262. /**
  263.  * Helper class
  264.  * Encapsulates the Windows specific initialization code and message processing
  265.  */
  266. class TrayWindowWrapper {
  267. public:
  268.     static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  269.  
  270. public:
  271.     TrayWindowWrapper(TrayWindow *window, HWND hwnd, const wchar_t *title);
  272.     ~TrayWindowWrapper();
  273.  
  274. private:
  275.      // hold raw pointer. Class instances point to TrayWindow anyway and destructed with it.
  276.     TrayWindow* mWindow;
  277.     
  278.     // HWND this wrapper is associated with
  279.     HWND mHwnd;
  280.     
  281.     // Previous window procedure
  282.     WNDPROC mOldWindowProc;
  283.     
  284.     // Win32 data structure containing the tray icon configuration
  285.     NOTIFYICONDATAW mIconData;
  286. };
  287.  
  288.  
  289. TrayWindowWrapper::TrayWindowWrapper(TrayWindow *aWindow, HWND aHwnd, const wchar_t *aTitle)
  290.     : mHwnd(aHwnd), mWindow(aWindow)
  291. {
  292.     // Hook window
  293.     ::SetPropW(mHwnd, kWrapperProp, this);
  294.     
  295.     // Only subclass once and leave this intact
  296.     if (::GetPropW(mHwnd, kWrapperOldProp) == NULL) {
  297.         WNDPROC oldWindowProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(
  298.             mHwnd,
  299.             GWLP_WNDPROC,
  300.             (LONG_PTR)(TrayWindowWrapper::WindowProc)
  301.         ));
  302.         ::SetPropW(mHwnd, kWrapperOldProp, oldWindowProc);
  303.     }
  304.     
  305.     // We need to get a minimize through.
  306.     // Otherwise the SFW/PM hack won't work
  307.     // However we need to protect against the watcher watching this
  308.     // XXX: Do it properly
  309.     void *watcher = reinterpret_cast<void*>(::GetPropW(mHwnd, kWatcherDOMProp));
  310.     ::RemovePropW(mHwnd, kWatcherDOMProp);
  311.     ::ShowWindow(mHwnd, SW_MINIMIZE);
  312.     if (watcher != 0) {
  313.         ::SetPropW(mHwnd, kWatcherDOMProp, watcher);
  314.     }    
  315.  
  316.     // Init the icon data according to MSDN
  317.     ZeroMemory(&mIconData, sizeof(mIconData));
  318.     mIconData.cbSize = sizeof(mIconData);
  319.  
  320.     // Copy the title
  321.     lstrcpynW(mIconData.szTip, aTitle, 127);
  322.     mIconData.szTip[128] = '\0'; // Better be safe than sorry :p
  323.  
  324.     // Get the window icon
  325.     HICON icon = reinterpret_cast<HICON>(::SendMessageW(mHwnd, WM_GETICON, ICON_SMALL, NULL));
  326.     if (icon == NULL) {
  327.         // Alternative method. Get from the window class
  328.         icon = reinterpret_cast<HICON>(::GetClassLongPtrW(mHwnd, GCLP_HICONSM));
  329.     }
  330.     // Alternative method: get the first icon from the main module (executable image of the process)
  331.     if (icon == NULL) {
  332.         icon = ::LoadIcon(GetModuleHandleW(NULL), MAKEINTRESOURCE(0));
  333.     }
  334.     // Alternative method. Use OS default icon
  335.     if (icon == NULL) {
  336.         icon = ::LoadIcon(NULL, IDI_APPLICATION);
  337.     }
  338.     mIconData.hIcon = icon;
  339.     
  340.     // Set the rest of the members
  341.     mIconData.hWnd = mHwnd;
  342.     mIconData.uCallbackMessage = WM_TRAYMESSAGE;
  343.     mIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
  344.     mIconData.uVersion = 5;
  345.  
  346.     // Install the icon
  347.     if (::Shell_NotifyIconW(NIM_ADD, &mIconData)) {
  348.         ::ShowWindow(mHwnd, SW_HIDE);
  349.     }
  350.     ::Shell_NotifyIconW(NIM_SETVERSION, &mIconData);
  351. }
  352. TrayWindowWrapper::~TrayWindowWrapper()
  353. {
  354.     // Remove kWrapperProp, so that the WindowProc knows not to handle any messages
  355.     ::RemovePropW(mHwnd, kWrapperProp);
  356.  
  357.     // Remove the icon
  358.     ::Shell_NotifyIconW(NIM_DELETE, &mIconData);
  359.  
  360.     // Show the window again
  361.     ::ShowWindow(mHwnd, SW_SHOW);
  362.  
  363.     // If it was minimized then restore it as well
  364.     if (::IsIconic(mHwnd)) {
  365.         ::ShowWindow(mHwnd, SW_RESTORE);
  366.     }
  367.  
  368.     // Try to grab focus
  369.     ::SetForegroundWindow(mHwnd);
  370. }
  371.  
  372. LRESULT CALLBACK TrayWindowWrapper::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  373. {
  374.     TrayWindowWrapper *me = reinterpret_cast<TrayWindowWrapper*>(GetPropW(hwnd, kWrapperProp));
  375.  
  376.     if (me == 0) {
  377.         // We should not process. Directly jump to default message passing
  378.         goto WrapperWindowProcEnd;
  379.     }
  380.  
  381.     // Do not switch messages; not constant
  382.     if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) {
  383.         // Got closed, no point in staying alive
  384.         me->mWindow->mService->Restore(me->mWindow->mDOMWindow);
  385.     }
  386.  
  387.     // The window might have been restored by someone else
  388.     else if (uMsg == WM_WINDOWPOSCHANGED && me->mHwnd == hwnd) {
  389.         // Check if somebody else tries to show us again
  390.         WINDOWPOS *pos = reinterpret_cast<WINDOWPOS*>(lParam);
  391.         if (pos && pos->flags & SWP_SHOWWINDOW) {
  392.             // shown again, unexpectedly that is, so release
  393.             me->mWindow->mService->Restore(me->mWindow->mDOMWindow);
  394.         }
  395.         /* XXX Fix this bit to something more reasonable
  396.             The following code kinda replicates the way mozilla gets the window state.
  397.             We intensionally "hide" the SW_SHOWMINIMIZED here.
  398.             This indeed might cause some side effects, but if it didn't we couldn't open
  399.             menus due to bugzilla #435848,.
  400.             This might defeat said bugfix completely reverting to old behavior, but only when we're active, of course.
  401.         */
  402.         if (pos->flags & SWP_FRAMECHANGED && ::IsWindowVisible(hwnd)) {
  403.             WINDOWPLACEMENT pl;
  404.             pl.length = sizeof(WINDOWPLACEMENT);
  405.             ::GetWindowPlacement(hwnd, &pl);
  406.             if (pl.showCmd == SW_SHOWMINIMIZED) {
  407.                 pl.showCmd = SW_HIDE;
  408.                 ::SetWindowPlacement(hwnd, &pl);
  409.                 return 0;
  410.             }
  411.         }
  412.     }
  413.     
  414.     // This is a badly documented custom broadcast message by explorer
  415.     else if (uMsg == WM_TASKBARCREATED) {
  416.         // The taskbar was (re)created. Add ourselves again.
  417.         Shell_NotifyIconW(NIM_ADD, &me->mIconData);
  418.     }
  419.     
  420.     // We got clicked. How exciting, isn't it.
  421.     else if (uMsg == WM_TRAYMESSAGE) {
  422.         nsString eventName;
  423.         PRUint16 button = 0;
  424.         switch (LOWORD(lParam)) {
  425.             case WM_LBUTTONUP:
  426.             case WM_MBUTTONUP:
  427.             case WM_RBUTTONUP:
  428.             case WM_CONTEXTMENU:
  429.             case NIN_KEYSELECT:
  430.                 eventName = NS_LITERAL_STRING("TrayClick");
  431.                 break;
  432.             case WM_LBUTTONDBLCLK:
  433.             case WM_MBUTTONDBLCLK:
  434.             case WM_RBUTTONDBLCLK:
  435.                 eventName = NS_LITERAL_STRING("TrayDblClick");
  436.                 break;
  437.         }
  438.         switch (LOWORD(lParam)) {
  439.             case WM_LBUTTONUP:
  440.             case WM_LBUTTONDBLCLK:
  441.                 button = 0;
  442.                 break;
  443.             case WM_MBUTTONUP:
  444.             case WM_MBUTTONDBLCLK:
  445.                 button = 1;
  446.                 break;
  447.             case WM_RBUTTONUP:
  448.             case WM_RBUTTONDBLCLK:
  449.             case WM_CONTEXTMENU:
  450.             case NIN_KEYSELECT:
  451.                 button = 2;
  452.                 break;
  453.         }
  454.         if (eventName.IsEmpty() == PR_FALSE) {
  455.             POINT wpt;
  456.             if (GetCursorPos(&wpt) == TRUE) {
  457.                 nsPoint pt((nscoord)wpt.x, (nscoord)wpt.y);
  458.  
  459.                 PRBool ctrlKey = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
  460.                 PRBool altKey = (::GetKeyState(VK_MENU) & 0x8000) != 0;
  461.                 PRBool shiftKey = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
  462.  
  463.                 // SFW/PM is a win32 hack, so that the context menu is hidden when loosing focus.
  464.                 ::SetForegroundWindow(hwnd);
  465.                 me->mWindow->DispatchMouseEvent(eventName, button, pt, ctrlKey, altKey, shiftKey);
  466.                 ::PostMessage(me->mHwnd, WM_NULL, 0, 0L);
  467.             }
  468.         }
  469.         return 0;
  470.     }
  471.     
  472.     // Window title changed
  473.     else if (uMsg == WM_SETTEXT) {
  474.         // First, let the original wndproc process this message,
  475.         // so that we may query the thing afterwards ;)
  476.         // this is required because we cannot know the encoding of this message for sure ;)
  477.         LRESULT rv;
  478.         WNDPROC oldWindowProc = reinterpret_cast<WNDPROC>(::GetPropW(hwnd, kWrapperOldProp));
  479.         if (oldWindowProc != NULL) {
  480.             rv = CallWindowProcW(oldWindowProc, hwnd, uMsg, wParam, lParam);
  481.         }
  482.         else {
  483.             rv = DefWindowProcW(hwnd, uMsg, wParam, lParam);
  484.         }
  485.  
  486.         if (::GetWindowTextW(hwnd, me->mIconData.szTip, 128) != 0) {
  487.             me->mIconData.szTip[128] = '\0';
  488.             Shell_NotifyIconW(NIM_MODIFY, &me->mIconData);
  489.         }
  490.         return rv;
  491.     }
  492.  
  493. WrapperWindowProcEnd:
  494.     // Default processing
  495.     WNDPROC oldWindowProc = reinterpret_cast<WNDPROC>(::GetPropW(hwnd, kWrapperOldProp));
  496.     if (oldWindowProc != NULL) {
  497.         return CallWindowProcW(oldWindowProc, hwnd, uMsg, wParam, lParam);
  498.     }
  499.     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
  500. }
  501.  
  502. NS_IMPL_ISUPPORTS0(TrayWindow)
  503.  
  504. TrayWindow::TrayWindow(TrayServiceImpl *aService)
  505. : mService(aService), mWrapper(nsnull), mDOMWindow(nsnull)
  506. {
  507. }
  508.  
  509. NS_IMETHODIMP TrayWindow::Destroy()
  510. {
  511.     // Deleting the wrapper will make it destroy any icon as well
  512.     delete mWrapper.forget();
  513.  
  514.     return NS_OK;
  515. }
  516.  
  517. NS_IMETHODIMP TrayWindow::Init(nsIDOMWindow *aWindow)
  518. {
  519.     NS_ENSURE_ARG_POINTER(aWindow);
  520.  
  521.     nsresult rv;
  522.  
  523.     nsCOMPtr<nsIBaseWindow> baseWindow;
  524.     rv = TrayServiceImpl_GetBaseWindow(aWindow, getter_AddRefs(baseWindow));
  525.     NS_ENSURE_SUCCESS(rv, rv);
  526.  
  527.     nativeWindow native = 0;
  528.     rv = baseWindow->GetParentNativeWindow(&native);
  529.     NS_ENSURE_SUCCESS(rv, rv);
  530.     
  531.     HWND hwnd = reinterpret_cast<HWND>(native);
  532.  
  533.     PRUnichar *title = nsnull;
  534.  
  535.     baseWindow->GetTitle(&title);
  536.  
  537.     mWrapper = new TrayWindowWrapper(this, hwnd, title);
  538.     mDOMWindow = aWindow;
  539.  
  540.     NS_Free(title);
  541.  
  542.     return NS_OK;
  543. }
  544.  
  545. NS_IMETHODIMP TrayWindow::GetWindow(nsIDOMWindow **aWindow)
  546. {
  547.     NS_ENSURE_ARG_POINTER(aWindow);
  548.     *aWindow = mDOMWindow;
  549.     NS_IF_ADDREF(*aWindow);
  550.  
  551.     return NS_OK;
  552. }
  553.  
  554. NS_IMETHODIMP TrayWindow::DispatchMouseEvent(const nsAString& aEventName, PRUint16 aButton, nsPoint& pt, PRBool aCtrlKey, PRBool aAltKey, PRBool aShiftKey)
  555. {
  556.     nsresult rv;
  557.  
  558.     nsCOMPtr<nsIDOMDocument> domDocument;
  559.     rv = mDOMWindow->GetDocument(getter_AddRefs(domDocument));
  560.     NS_ENSURE_SUCCESS(rv, rv);
  561.  
  562.     nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(domDocument));
  563.     nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(domDocument));
  564.     NS_ENSURE_TRUE(docEvent && target, NS_ERROR_INVALID_ARG);
  565.  
  566.     nsCOMPtr<nsIDOMDocumentView> docView(do_QueryInterface(domDocument, &rv));
  567.     NS_ENSURE_SUCCESS(rv, rv);
  568.     
  569.     nsCOMPtr<nsIDOMAbstractView> abstractView; 
  570.     rv = docView->GetDefaultView(getter_AddRefs(abstractView));
  571.     NS_ENSURE_SUCCESS(rv, rv);
  572.  
  573.     nsCOMPtr<nsIDOMEvent> event;
  574.     rv = docEvent->CreateEvent(NS_LITERAL_STRING("MouseEvents"), getter_AddRefs(event));
  575.     NS_ENSURE_SUCCESS(rv, rv);
  576.  
  577.     nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(event, &rv));
  578.     NS_ENSURE_SUCCESS(rv, rv);
  579.  
  580.     rv = mouseEvent->InitMouseEvent(
  581.         aEventName,
  582.         PR_FALSE,
  583.         PR_TRUE,
  584.         abstractView,
  585.         0,
  586.         pt.x,
  587.         pt.y,
  588.         0,
  589.         0,
  590.         aCtrlKey,
  591.         aAltKey,
  592.         aShiftKey,
  593.         PR_FALSE,
  594.         aButton,
  595.         target
  596.         );
  597.     NS_ENSURE_SUCCESS(rv, rv);
  598.  
  599.     PRBool dummy;
  600.     return target->DispatchEvent(mouseEvent, &dummy);
  601. }
  602.  
  603. NS_IMPL_ISUPPORTS2(TrayServiceImpl, nsIObserver, trayITrayService)
  604.  
  605. TrayServiceImpl::TrayServiceImpl()
  606. {
  607.     // Register our hidden window
  608.     // Get TaskbarCreated
  609.     // OK, this is doubled, but doesn't matter
  610.     WM_TASKBARCREATED = RegisterWindowMessageW(L"TaskbarCreated");
  611.     // We register this as well, as we cannot know which WM_USER values are already taken
  612.     WM_TRAYMESSAGE = RegisterWindowMessageW(kTrayMessage);
  613.     
  614.     // Vista (Administrator) needs some love, or else we won't receive anything due to UIPI
  615.     if (OSVersionInfo().isVistaOrLater()) {
  616.         TrayServiceImpl_AdjustMessageFilters(MSGFLT_ADD);
  617.     }
  618.  
  619.     // Observe when the app is going down.
  620.     // Else we might not properly clean up
  621.     // And leave some tray icons behind
  622.     nsresult rv;
  623.     nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1", &rv));
  624.     if (NS_SUCCEEDED(rv)) {
  625.         obs->AddObserver(static_cast<nsIObserver*>(this), "xpcom-shutdown", PR_FALSE);
  626.     }
  627.     
  628. }
  629. TrayServiceImpl::~TrayServiceImpl()
  630. {
  631.     Destroy();
  632. }
  633. void TrayServiceImpl::Destroy()
  634. {
  635.     // Destroy remaining icons
  636.     PRInt32 count = mWindows.Count();
  637.     for (PRInt32 i = count - 1; i > -1; --i) {
  638.         mWindows[i]->Destroy();
  639.         ReleaseTrayWindow(mWindows[i]);
  640.     }
  641.     mWindows.Clear();
  642.     mWatches.Clear();
  643.  
  644.     // Vista (Administrator) needs some unlove, see c'tor
  645.     if (OSVersionInfo().isVistaOrLater()) {
  646.         TrayServiceImpl_AdjustMessageFilters(MSGFLT_REMOVE);
  647.     }
  648. }
  649.  
  650. NS_IMETHODIMP TrayServiceImpl::Minimize(nsIDOMWindow *aWindow)
  651. {
  652.     NS_ENSURE_ARG_POINTER(aWindow);
  653.     
  654.     nsresult rv;
  655.     nsCOMPtr<TrayWindow> trayWindow;
  656.     rv = FindTrayWindow(aWindow, getter_AddRefs(trayWindow));
  657.     if (NS_SUCCEEDED(rv)) {
  658.         return NS_ERROR_ALREADY_INITIALIZED;
  659.     }
  660.  
  661.     trayWindow = new TrayWindow(this);
  662.     if (trayWindow == nsnull) {
  663.         return NS_ERROR_OUT_OF_MEMORY;
  664.     }
  665.     rv = trayWindow->Init(aWindow);
  666.     NS_ENSURE_SUCCESS(rv, rv);
  667.  
  668.     if (mWindows.AppendObject(trayWindow) == PR_FALSE) {
  669.         return NS_ERROR_FAILURE;
  670.     }
  671.     DispatchTrustedEvent(aWindow, NS_LITERAL_STRING("TrayMinimize"));
  672.     return NS_OK;
  673. }
  674.  
  675. NS_IMETHODIMP TrayServiceImpl::Restore(nsIDOMWindow *aWindow)
  676. {
  677.     NS_ENSURE_ARG_POINTER(aWindow);
  678.  
  679.     nsresult rv;
  680.     nsCOMPtr<TrayWindow> trayWindow;
  681.     rv = FindTrayWindow(aWindow, getter_AddRefs(trayWindow));
  682.     if (NS_FAILED(rv)) {
  683.         return NS_OK;
  684.     }
  685.  
  686.     rv = trayWindow->Destroy();
  687.     NS_ENSURE_SUCCESS(rv, rv);
  688.  
  689.     rv = ReleaseTrayWindow(trayWindow);
  690.     NS_ENSURE_SUCCESS(rv, rv);
  691.  
  692.     DispatchTrustedEvent(aWindow, NS_LITERAL_STRING("TrayRestore"));
  693.  
  694.     return NS_OK;
  695. }
  696.  
  697. NS_IMETHODIMP TrayServiceImpl::RestoreAll()
  698. {
  699.     nsresult rv;
  700.     PRInt32 count = mWindows.Count();
  701.  
  702.     for (PRInt32 i = count - 1; i > -1; --i) {
  703.         
  704.         nsCOMPtr<nsIDOMWindow> window;
  705.         rv = mWindows[i]->GetWindow(getter_AddRefs(window));
  706.         NS_ENSURE_SUCCESS(rv, rv);
  707.  
  708.         rv = mWindows[i]->Destroy();
  709.         NS_ENSURE_SUCCESS(rv, rv);
  710.  
  711.         rv = ReleaseTrayWindow(mWindows[i]);
  712.         NS_ENSURE_SUCCESS(rv, rv);
  713.  
  714.         DispatchTrustedEvent(window, NS_LITERAL_STRING("TrayRestore"));
  715.     }
  716.     return NS_OK;
  717. }
  718.  
  719. NS_IMETHODIMP TrayServiceImpl::WatchMinimize(nsIDOMWindow *aWindow)
  720. {
  721.     NS_ENSURE_ARG_POINTER(aWindow);
  722.  
  723.     PRInt32 index = mWatches.IndexOf(aWindow);
  724.     if (index != -1) {
  725.         return NS_OK;
  726.     }
  727.  
  728.     nsresult rv;
  729.  
  730.     nsCOMPtr<nsIBaseWindow> baseWindow;
  731.     rv = TrayServiceImpl_GetBaseWindow(aWindow, getter_AddRefs(baseWindow));
  732.     NS_ENSURE_SUCCESS(rv, rv);
  733.  
  734.     nativeWindow native = 0;
  735.     rv = baseWindow->GetParentNativeWindow(&native);
  736.     NS_ENSURE_SUCCESS(rv, rv);
  737.     
  738.     HWND hwnd = reinterpret_cast<HWND>(native);
  739.  
  740.     // Storing this as raw pointer shouldn't be a problem, as the lifetime of the DOMWindow is at least as long as of the BaseWindow
  741.     ::SetPropW(hwnd, kWatcherDOMProp, reinterpret_cast<HANDLE>(aWindow));
  742.  
  743.     // We only subclass once
  744.     if (::GetPropW(hwnd, kWatcherOldProp) == NULL) {
  745.         WNDPROC oldProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(TrayServiceImpl_WatchWindowProc)));
  746.         ::SetPropW(hwnd, kWatcherOldProp, reinterpret_cast<HANDLE>(oldProc));
  747.     }
  748.  
  749.     return NS_OK;
  750. }
  751. NS_IMETHODIMP TrayServiceImpl::UnwatchMinimize(nsIDOMWindow *aWindow)
  752. {
  753.     NS_ENSURE_ARG_POINTER(aWindow);
  754.  
  755.     nsresult rv;
  756.  
  757.     mWatches.RemoveObject(aWindow);
  758.  
  759.     nsCOMPtr<nsIBaseWindow> baseWindow;
  760.     rv = TrayServiceImpl_GetBaseWindow(aWindow, getter_AddRefs(baseWindow));
  761.     NS_ENSURE_SUCCESS(rv, rv);
  762.  
  763.     nativeWindow native = 0;
  764.     rv = baseWindow->GetParentNativeWindow(&native);
  765.     NS_ENSURE_SUCCESS(rv, rv);
  766.     
  767.     HWND hwnd = reinterpret_cast<HWND>(native);
  768.  
  769.     // This will effectively cause the WindowProc not to carry out anything itself.
  770.     // We cannot simply remove that window proc, as somebody else might have put another in and chained us
  771.     ::RemovePropW(hwnd, kWatcherDOMProp);
  772.  
  773.  
  774.     return NS_OK;
  775. }
  776.  
  777. NS_IMETHODIMP TrayServiceImpl::IsWatchedWindow(nsIDOMWindow *aWindow, PRBool *aResult)
  778. {
  779.     NS_ENSURE_ARG_POINTER(aWindow);
  780.     NS_ENSURE_ARG_POINTER(aResult);
  781.     
  782.     *aResult = mWatches.IndexOfObject(aWindow) != -1 ? PR_TRUE : PR_FALSE;
  783.     return NS_OK;
  784. }
  785.  
  786. NS_IMETHODIMP TrayServiceImpl::Observe(nsISupports *, const char *aTopic, const PRUnichar *)
  787. {
  788.     if (lstrcmpA(aTopic, "xpcom-shutdown") == 0) {
  789.         Destroy();
  790.  
  791.         nsresult rv;
  792.         nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1", &rv));
  793.         if (NS_SUCCEEDED(rv)) {
  794.             obs->RemoveObserver(static_cast<nsIObserver*>(this), "xpcom-shutdown");
  795.         }
  796.     }
  797.     return NS_OK;
  798. }
  799.  
  800. NS_IMETHODIMP TrayServiceImpl::ReleaseTrayWindow(TrayWindow *aWindow)
  801. {
  802.     NS_ENSURE_ARG_POINTER(aWindow);
  803.     mWindows.RemoveObject(aWindow);
  804.     return NS_OK;
  805. }
  806.  
  807.  
  808. NS_IMETHODIMP TrayServiceImpl::FindTrayWindow(nsIDOMWindow *aWindow, TrayWindow **aTrayWindow)
  809. {
  810.     NS_ENSURE_ARG_POINTER(aWindow);
  811.     NS_ENSURE_ARG_POINTER(aTrayWindow);
  812.     
  813.     nsresult rv;
  814.     nsCOMPtr<nsIDOMWindow> domWindow;
  815.     PRInt32 count = mWindows.Count();
  816.     
  817.     for (PRInt32 i = 0; i < count; ++i) {
  818.         rv = mWindows[i]->GetWindow(getter_AddRefs(domWindow));
  819.         if (NS_FAILED(rv)) {
  820.             continue;
  821.         }
  822.         if (domWindow == aWindow) {
  823.             *aTrayWindow = mWindows[i];
  824.             NS_IF_ADDREF(*aTrayWindow);
  825.             return NS_OK;
  826.         }
  827.     }
  828.     return NS_ERROR_FAILURE;
  829. }
  830.  
  831. NS_IMETHODIMP TrayServiceImpl::DispatchTrustedEvent(nsIDOMWindow *aWindow, const nsAString& aEventName)
  832. {
  833.     NS_ENSURE_ARG_POINTER(aWindow);
  834.  
  835.     nsresult rv;
  836.  
  837.     nsCOMPtr<nsIDOMDocument> domDocument;
  838.     rv = aWindow->GetDocument(getter_AddRefs(domDocument));
  839.     NS_ENSURE_SUCCESS(rv, rv);
  840.  
  841.     nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(domDocument));
  842.     nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(domDocument));
  843.     NS_ENSURE_TRUE(docEvent && target, NS_ERROR_INVALID_ARG);
  844.  
  845.     nsCOMPtr<nsIDOMEvent> event;
  846.     rv = docEvent->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
  847.     NS_ENSURE_SUCCESS(rv, rv);
  848.     
  849.     nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event, &rv));
  850.     NS_ENSURE_SUCCESS(rv, rv);
  851.     
  852.     rv = event->InitEvent(aEventName, PR_FALSE, PR_TRUE);
  853.     NS_ENSURE_SUCCESS(rv, rv);
  854.     
  855.     rv = privateEvent->SetTrusted(PR_TRUE);
  856.     NS_ENSURE_SUCCESS(rv, rv);
  857.  
  858.     PRBool dummy;
  859.     return target->DispatchEvent(event, &dummy);
  860. }
  861.